package com.sweet.lang;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 实体类工具
 *
 * @author limi
 * @version 2017-08-03-18:38
 */
public final class ObjectUtil {
    /**
     * 数字类型字段
     */
    private static final List<Class> numberFieldClasses = new ArrayList<>();

    static {
        numberFieldClasses.add(Byte.class);
        numberFieldClasses.add(byte.class);
        numberFieldClasses.add(Short.class);
        numberFieldClasses.add(short.class);
        numberFieldClasses.add(Integer.class);
        numberFieldClasses.add(int.class);
        numberFieldClasses.add(Long.class);
        numberFieldClasses.add(long.class);
        numberFieldClasses.add(Float.class);
        numberFieldClasses.add(float.class);
        numberFieldClasses.add(Double.class);
        numberFieldClasses.add(double.class);
    }

    /**
     * 合并列表中的对象相同属性的值
     *
     * @param datas
     * @param ignoreFields
     * @return
     */
    public static Object addSameNumberField(List<?> datas, List<String> ignoreFields) {
        //System.out.println("=====================");
        //System.out.println("合并前：" + JsonUtil.toJson(datas));

        Object result = null;
        for (Object data : datas) {
            if (result == null) {
                result = data;
            } else {
                result = addSameNumberField(data, result, ignoreFields);
            }
        }

        //System.out.println("合并后：" + JsonUtil.toJson(result));
        return result;
    }

    /**
     * 合并两个对象的相同属性的值
     *
     * @param sourceBean   数据来源bean
     * @param targetBean   合入的目标bean，因此以目标bean的字段列表为准，在sourceBean中检测
     * @param ignoreFields 要忽略的字段
     * @return
     */
    public static Object addSameNumberField(Object sourceBean, Object targetBean, List<String> ignoreFields) {
        //定义Class
        Class sourceClass = sourceBean.getClass();
        Class targetClass = targetBean.getClass();

        //获取targetClass字段列表,无字段则原样返回
        Field[] targetFields = targetClass.getDeclaredFields();
        if (ArrayUtil.isEmpty(targetFields)) {
            return targetBean;
        }

        //System.out.println("targetField=" + JsonUtil.toJson(targetFields));

        //定义数据值
        Object sourceValue;
        Object targetValue;
        Object resultValue;

        //检验并合并字段
        String fieldName;
        Field sourceField;
        for (Field targetField : targetFields) {
            //设置可访问，否则无法获取和设置值
            targetField.setAccessible(true);

            //获取字段名,验证是否需要忽略，加入特殊忽略字段：DEFAULT_INITIAL_CAPACITY为集合容量为int型;serialVersionUID是序列化标记为long型
            fieldName = targetField.getName();
            if (ignoreFields.contains(fieldName) || fieldName.equals("serialVersionUID") || fieldName.equals("DEFAULT_INITIAL_CAPACITY")) {
                continue;
            }

            //检查是否允许字段合并(fastJson从string转为对象，若对象被定义为Object，则转换后为map而不是bean，因此允许map被合并)
            if (targetField.getType() != Map.class && !numberFieldClasses.contains(targetField.getType())) {
                continue;
            }

            //从源数据中获取字段值
            try {
                sourceField = sourceClass.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                sourceField = null;
            }

            //字段类型不一致，则不处理
            if (sourceField == null || sourceField.getType() != targetField.getType()) {
                continue;
            }

            //设置可访问，否则无法获取和设置值
            sourceField.setAccessible(true);

            try {
                //获取源及目标值
                sourceValue = sourceField.get(sourceBean);
                targetValue = targetField.get(targetBean);

                //数据合并
                if (targetField.getType() == Map.class) {
                    //使用map形式合并
                    resultValue = addMapSameNumberKey((Map<Object, Object>) sourceValue, (Map<Object, Object>) targetValue);
                } else {
                    //使用纯数值形式合并
                    resultValue = addNumber(sourceValue, targetValue);
                }

                //重新填充值
                targetField.set(targetBean, resultValue);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        return targetBean;
    }

    /**
     * 数值相加
     *
     * @param num1
     * @param num2
     * @return
     */
    private static final Object addNumber(Object num1, Object num2) {
        Class cls1 = num1.getClass();
        Class cls2 = num2.getClass();

        //依次判断并相加
        if (cls1 == Double.class || cls2 == Double.class) {
            return (Double) num1 + (Double) num2;
        }
        if (cls1 == Float.class || cls2 == Float.class) {
            return (Float) num1 + (Float) num2;
        }
        if (cls1 == Long.class || cls2 == Long.class) {
            return (Long) num1 + (Long) num2;
        }
        if (cls1 == Integer.class || cls2 == Integer.class) {
            return (Integer) num1 + (Integer) num2;
        }
        if (cls1 == Short.class || cls2 == Short.class) {
            return (Short) num1 + (Short) num2;
        }
        if (cls1 == Byte.class || cls2 == Byte.class) {
            return (Byte) num1 + (Byte) num2;
        }

        return 0;
    }

    /**
     * 合并两个相同map的值
     *
     * @param sourceMap
     * @param targetMap
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> Map<K, V> addMapSameNumberKey(Map<K, V> sourceMap, Map<K, V> targetMap) {
        V sourceValue;
        V targetValue;
        for (Map.Entry<K, V> target : targetMap.entrySet()) {
            //获取目标值
            targetValue = target.getValue();
            if (targetValue == null || !numberFieldClasses.contains(targetValue.getClass())) {
                continue;
            }

            //获取源数据值
            sourceValue = sourceMap.get(target.getKey());
            if (sourceValue == null || !numberFieldClasses.contains(sourceValue.getClass())) {
                continue;
            }

            //合并数据
            targetMap.put(target.getKey(), (V) addNumber(target.getValue(), sourceValue));
        }

        return targetMap;
    }

//    public static void main(String arg[]) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
//        RoleGoldInOut sourceBean = new RoleGoldInOut();
//        sourceBean.setDatetime("2017-01-01");
//        sourceBean.setPlatform("yunmiao");
//        sourceBean.setIn(100);
//        sourceBean.setOut(200);
//
////        RoleSingle sourceBean = new RoleSingle();
////        sourceBean.setDatetime("2017-01-02");
////        sourceBean.setPlatform("360");
////        sourceBean.setTotal(10);
//
//        //同类型
//        RoleGoldInOut targetBean = new RoleGoldInOut();
//        targetBean.setDatetime("2017-01-02");
//        targetBean.setPlatform("360");
//        targetBean.setIn(10);
//        targetBean.setOut(20);
//
////        RoleSingle targetBean = new RoleSingle();
////        targetBean.setDatetime("2017-01-02");
////        targetBean.setPlatform("360");
////        targetBean.setTotal(10);
//
//        List<String> ignoreField = new ArrayList<>();
//        ignoreField.add("datetime");
//        ignoreField.add("platform");
//
//        Object result = addSameNumberField(sourceBean, targetBean, ignoreField);
//        System.out.println("合并Bean结果:" + JsonUtil.toJson(result));
//
//        Map<String, Integer> map1 = new HashMap<>();
//        map1.put("max", 1);
//        map1.put("avg", 2);
//        map1.put("other", 3);
//
//        Map<String, Integer> map2 = new HashMap<>();
//        map2.put("max", 3);
//        map2.put("avg", 4);
//        map2.put("sum", 5);
//
//        Object resultMap = addMapSameNumberKey(map1, map2);
//        System.out.println("合并Map结果:" + JsonUtil.toJson(resultMap));
//    }
}
